import { defineStore, type _Method } from 'pinia'
import { v4 as uuidv4 } from 'uuid'
import { cloneDeep } from 'lodash-es'
import { textDefaultProps, imageDefaultProps } from '@/constant/defaultProps'
import { message } from 'ant-design-vue'
import { insertAt } from '@/constant/helper'
import { WorkApi } from '@/api/works'
import type { ListData } from 'axios'

export const testComponents: ComponentData[] = [
  {
    id: uuidv4(),
    name: 'LText',
    layerName: '图层1',
    props: {
      ...textDefaultProps,
      text: 'hello',
      fontSize: '20px',
      color: '#000000',
      lineHeight: '1',
      textAlign: 'left',
      fontFamily: '',
      width: '80px',
      height: '80px',
      backgroundColor: '#efefef',
      left: '10px',
      top: '10px'
    }
  }
  // {
  //   id: uuidv4(),
  //   name: 'LText',
  //   layerName: '图层2',
  //   props: {
  //     ...textDefaultProps,
  //     text: 'hello2',
  //     fontSize: '10px',
  //     fontWeight: 'bold',
  //     lineHeight: '2',
  //     textAlign: 'left',
  //     fontFamily: ''
  //   }
  // },
  // {
  //   id: uuidv4(),
  //   name: 'LText',
  //   layerName: '图层3',
  //   props: {
  //     ...textDefaultProps,
  //     text: 'hello3',
  //     fontSize: '15px',
  //     actionType: 'url',
  //     url: 'https://www.baidu.com',
  //     lineHeight: '3',
  //     textAlign: 'left',
  //     fontFamily: '',
  //     color: 'red'
  //   }
  // },
  // {
  //   id: uuidv4(),
  //   name: 'LImage',
  //   layerName: '图层4',
  //   props: {
  //     ...imageDefaultProps,
  //     imgSrc: 'http://p-test-ui.oss-cn-beijing.aliyuncs.com/folder-test/arvObR.png',
  //     width: '60px'
  //   }
  // }
]

const pageDefaultProps = {
  backgroundColor: '#ffffff',
  backgroundImage: 'url(http://p-test-ui.oss-cn-beijing.aliyuncs.com/folder-test/arvObR.png)',
  backgroundRepeat: 'no-repeat',
  backgroundSize: 'cover',
  height: '560px'
}

const modifyHistory = (state: EditorDataProps, history: HistoryProps, type: 'undo' | 'redo') => {
  const { componentId, data } = history
  const { key, oldValue, newValue } = data
  // modify the page setting
  if (!componentId) {
    state.page.props[key as keyof PageProps] = type === 'undo' ? oldValue : newValue
  } else {
    const newKey = key as keyof AllComponentTypes | Array<keyof AllComponentTypes>
    const updatedComponent = state.components.find((component) => component.id === componentId)
    if (updatedComponent) {
      if (Array.isArray(newKey)) {
        newKey.forEach((keyName, index) => {
          updatedComponent.props[keyName] = type === 'undo' ? oldValue[index] : newValue[index]
        })
      } else {
        updatedComponent.props[newKey] = type === 'undo' ? oldValue : newValue
      }
    }
  }
}

const debounceChange = (callback: (...args: any) => void, timeout = 1000) => {
  let timer = 0
  return (...args: any) => {
    clearTimeout(timer)
    timer = window.setTimeout(() => {
      callback(...args)
    }, timeout)
  }
}

const pushHistory = (state: EditorDataProps, historyRecord: HistoryProps) => {
  //check historyIndex is already moved
  if (state.historyIndex !== -1) {
    //if moved, delete all the records greater thanthe index
    state.histories = state.histories.slice(0, state.historyIndex)
    // move historyIndex to unmoved
    state.historyIndex = -1
  }
  // check length
  if (state.histories.length < state.maxHistoryNumber) {
    state.histories.push(historyRecord)
  } else {
    state.histories.shift()
    state.histories.push(historyRecord)
  }
}

const pushModifyHistory = (
  state: EditorDataProps,
  { key, value, id }: UpdateComponentData,
  oldValue: any
) => {
  if (!state.cachedOldValues) {
    state.cachedOldValues = oldValue
  }
  pushHistory(state, {
    id: uuidv4(),
    componentId: id || state.currentElement,
    type: 'modify',
    data: { oldValue: state.cachedOldValues, newValue: value, key }
  })
  state.cachedOldValues = null
}

const pushHistoryDebounce = debounceChange(pushModifyHistory)

const setDirtyWrapper = (callback: _Method) => {
  return (state: EditorDataProps, payload: any) => {
    state.isDirty = true
    callback(payload)
  }
}

/**
 * 模板列表
 * @methods setTemplatesList 设置模板数据
 */
export const editorInfos = defineStore('editors', {
  state: (): EditorDataProps => ({
    components: testComponents,
    currentElement: '',
    page: {
      props: pageDefaultProps,
      title: '',
      setting: {}
    },
    histories: [],
    historyIndex: -1,
    cachedOldValues: null,
    maxHistoryNumber: 5,
    isDirty: false,
    channels: []
  }),
  actions: {
    //
    setActive(id: string) {
      this.currentElement = id
    },
    // reset editor to clear
    resetEditor() {
      this.page = { props: pageDefaultProps, title: '', setting: {} }
      this.components = []
      this.currentElement = ''
      this.historyIndex = -1
      this.histories = []
    },
    addComponent(component: ComponentData) {
      component.layerName = '图层' + (this.components.length + 1)
      this.components.push(component)
      pushHistory(this, {
        id: uuidv4(),
        componentId: component.id,
        type: 'add',
        data: cloneDeep(component)
      })
      this.isDirty = true
    },
    copyComponent(id: string) {
      const currentComponent = this.getElement(id)
      if (currentComponent) {
        this.copiedComponent = currentComponent
        message.success('已拷贝当前图层', 1)
      }
    },
    pasteCopiedComponent() {
      if (this.copiedComponent) {
        const clone = cloneDeep(this.copiedComponent)
        clone.id = uuidv4()
        clone.layerName = clone.layerName + '副本'
        this.components.push(clone)
        message.success('已黏贴当前图层', 1)
        pushHistory(this, {
          id: uuidv4(),
          componentId: clone.id,
          type: 'add',
          data: cloneDeep(clone)
        })
        this.isDirty = true
      }
    },
    deleteComponent(id: string) {
      const currentComponent = this.getElement(id)
      if (currentComponent) {
        const currentIndex = this.components.findIndex(
          (component) => component.id == id || this.currentElement
        )
        this.components = this.components.filter((component) => component.id !== id)
        pushHistory(this, {
          id: uuidv4(),
          componentId: currentComponent.id,
          type: 'delete',
          data: currentComponent,
          index: currentIndex
        })
        message.success('删除当前图层成功', 1)
        this.isDirty = true
      }
    },
    moveComponent(data: { direction: MoveDirection; amount: number; id: string }) {
      const currentComponent = this.getElement(data.id)
      if (currentComponent) {
        const oldTop = parseInt(currentComponent.props.top || '0')
        const oldLeft = parseInt(currentComponent.props.left || '0')
        const { direction, amount, id } = data
        switch (direction) {
          case 'Up': {
            const newValue = oldTop - amount + 'px'
            this.updateComponent({ key: 'top', value: newValue, id })
            break
          }
          case 'Down': {
            const newValue = oldTop + amount + 'px'
            this.updateComponent({ key: 'top', value: newValue, id })
            break
          }
          case 'Left': {
            const newValue = oldLeft - amount + 'px'
            this.updateComponent({ key: 'left', value: newValue, id })
            break
          }
          case 'Right': {
            const newValue = oldLeft + amount + 'px'
            this.updateComponent({ key: 'left', value: newValue, id })
            break
          }
          default:
            break
        }
        this.isDirty = true
      }
    },
    updateComponent({ key, value, id, isRoot }: UpdateComponentData) {
      const updateComponent = this.components.find(
        (component) => component.id === (id || this.currentElement)
      )
      if (updateComponent) {
        if (isRoot) {
          ;(updateComponent as any)[key as string] = value
        } else {
          const oldValue = Array.isArray(key)
            ? key.map((key) => updateComponent.props[key])
            : updateComponent.props[key]

          pushHistoryDebounce(this, { key, value, id }, oldValue)
          if (Array.isArray(key) && Array.isArray(value)) {
            key.forEach((keyName, index) => {
              updateComponent.props[keyName] = value[index]
            })
          } else if (typeof key === 'string' && typeof value === 'string') {
            updateComponent.props[key] = value
          }
        }
        this.isDirty = true
      }
    },
    updatePage({ key, value, level }: any) {
      const pageData = this.page as { [key: string]: any }
      if (level) {
        if (level === 'props') {
          const oldValue = pageData[level][key]
          pushHistoryDebounce(this, { key, value }, oldValue)
        }
        pageData[level][key] = value
      } else {
        pageData[key] = value
      }
      this.isDirty = true
    },
    undo() {
      if (this.historyIndex === -1) {
        this.historyIndex = this.histories.length - 1
      } else {
        this.historyIndex--
      }
      // get the record
      const history = this.histories[this.historyIndex]
      switch (history.type) {
        case 'add':
          // if we create a component, then we should remove it
          this.components = this.components.filter(
            (component) => component.id !== history.componentId
          )
          break
        case 'delete':
          // if we delete a component, we should restore it at the right position
          this.components = insertAt(this.components, history.index as number, history.data)
          break
        case 'modify': {
          modifyHistory(this, history, 'undo')
          break
        }
        default:
          break
      }
    },
    redo() {
      // can't redo when historyIndex is the last item or historyIndex is never moved
      if (this.historyIndex === -1) {
        return
      }
      // get the record
      const history = this.histories[this.historyIndex]
      // process the history data
      switch (history.type) {
        case 'add':
          this.components.push(history.data)
          // this.components = insertAt(this.components, history.index as number, history.data)
          break
        case 'delete':
          this.components = this.components.filter(
            (component) => component.id !== history.componentId
          )
          break
        case 'modify': {
          modifyHistory(this, history, 'redo')
          break
        }
        default:
          break
      }
      this.historyIndex++
    },
    //获取模板列表接口
    getApiTemplates() {},
    async getWork(curWorkId: number) {
      const [e, r] = await WorkApi.getWork<workData>(curWorkId, {
        opName: 'getWork'
      })
      if (r && r.errno === 0) {
        const { content, ...rest } = r.data
        this.page = { ...this.page, ...rest }
        if (content && content.props) {
          this.page.props = { ...this.page.props, ...content.props }
        }
        if (content && content.setting) {
          this.page.setting = { ...this.page.setting, ...content.setting }
        }
        this.components = content.components
      }
    },
    async saveWork(cureWorkId: string) {
      const { title, desc, props, coverImg, setting } = this.page
      let ndata
      const postData = {
        title,
        desc,
        coverImg,
        content: {
          components: this.components,
          props,
          setting
        }
      }
      const [e, r] = await WorkApi.saveWork(cureWorkId, postData, {
        opName: 'saveWork'
      })
      if (r && r.errno === 0) {
        ndata = r.data
        this.isDirty = false
        this.page.updatedAt = new Date().toISOString()
      }
      return ndata
    },
    async publishWork(cureWorkId: string) {
      await WorkApi.publishWork(
        cureWorkId,
        {},
        {
          opName: 'publishWork'
        }
      )
    },
    async publishTemplate(cureWorkId: string) {
      const [e, r] = await WorkApi.publishTemplate(
        cureWorkId,
        {},
        {
          opName: 'publishTemplate'
        }
      )
      return r
    },
    async fetchChannels(cureWorkId: string) {
      const [e, r] = await WorkApi.getWorkChannels<ListData<ChannelProps>>(cureWorkId, {
        opName: 'publishWork'
      })
      if (r && r.errno === 0) {
        this.channels = r.data.list
      }
    },
    async createChannel(payload: {}) {
      const [e, r] = await WorkApi.createWorkChannels<ChannelProps>(payload, {
        opName: 'publishWork'
      })
      if (r && r.errno === 0) {
        //解决watch 问题，因数组是引用类型，内容改变不会触发，解决方法之一可以替换掉数组
        //this.channels.push(r.data)
        this.channels = [...this.channels, r.data]
      }
    },
    async deleteChannel(id: string) {
      const [e, r] = await WorkApi.deleteWorkChannel(id, {
        opName: 'deleteChannel'
      })
      if (r && r.errno === 0) {
        this.channels = this.channels.filter((channel) => channel.id !== id)
      }
    }
  },

  getters: {
    getCurrentElement: (state) => {
      return () => state.components.find((t) => t.id === state.currentElement)
    },
    getElement: (state) => {
      return (id: string) => state.components.find((t) => t.id === (id || state.currentElement))
    },
    checkUndoDisable: (state) => {
      // 1 no history item
      // 2 move to the first item
      if (state.histories.length === 0 || state.historyIndex === 0) {
        return true
      }
      return false
    },
    checkRedoDisable: (state) => {
      // 1 no history item
      // 2 move to the last item
      // 3 never undo before
      if (
        state.histories.length === 0 ||
        state.historyIndex === state.histories.length ||
        state.historyIndex === -1
      ) {
        return true
      }
      return false
    }
  }
})
